<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="./d3.v5.min.js"></script>
  <style>
    #app {
      width: 600px;
      height: 500px;
      margin: 0 auto;
    }

    .tool {
      text-align: center;
    }
  </style>
</head>


<body>

  <div id="app"></div>
  <hr>
  <div class="tool">
    <button id="btn-add">add</button>
    <button id="btn-update">update</button>
  </div>


  <script>
    var container = d3.select('#app');



    function Bar(optinos) {
      this.padding = 10;
      this.optinos = optinos;
      this.datalist = optinos.data;
      this.container = d3.select(optinos.el);
      this.sort_flag = false;
      this.init()
    }

    Bar.prototype = {
      init() {
        this.initSVG()
        this.initScale();
        this.renderRect();
        this.renderText();
        this.initEvent();
      },
      //初始化画布
      initSVG() {
        this.svg = this.container.append('svg')
          .attr('width', this.optinos.width)
          .attr('height', this.optinos.height)
          .style('background', '#efefef');
      },
      initEvent() {
        d3.select("#btn-add").on('click', () => {
          let num = Math.ceil(Math.random() * 450);
          this.datalist.push(num);
          this.initScale();
          this.renderText();
          this.renderRect();
        })


        d3.select("#btn-update").on('click', () => {
          this.datalist = mockData();
          this.initScale();
          this.renderText();
          this.renderRect();
        })
      },
      //绘制柱状图
      renderRect() {

        //添加新数据
        this.svg.selectAll('rect')
          .data(this.datalist)
          .enter()
          .append('rect')
          .style('fill', 'green')
          .on('mouseover', function (d, i) {
            d3.select(this).style('fill', 'red')
          })
          .on('mouseout', function (d, i) {
            d3.select(this).style('fill', 'green')
          })
          .on('click', (d, i) => {
            this.sort();
          })


        //数据更新
        this.svg.selectAll('rect')
          .data(this.datalist)
          .attr('width', this.x_scale.bandwidth())
          .attr('x', (d, i) => {
            return this.x_scale(i);
          })
          .attr('y', (d) => {
            return this.optinos.height - this.y_scale(d);
          })
          .attr('height', (d, i) => {
            return this.y_scale(d);
          })
          .exit().remove();

      },

      //添加文本信息
      renderText() {
        //添加新数据
        this.svg.selectAll('text')
          .data(this.datalist)
          .enter()
          .append('text')
          .text(function (d, i) {
            return d;
          })
          .style('fill', 'orange')


        //数据更新
        this.svg.selectAll('text')
          .data(this.datalist)
          .attr('x', (d, i) => {
            return this.x_scale(i) + this.x_scale.bandwidth() / 2;
          })
          .attr('y', (d) => {
            return this.optinos.height - this.y_scale(d) - 5;
          })
          .attr('text-anchor', 'middle')
          .exit().remove();
      },

      sort() {
        this.svg.selectAll('rect').sort((a, b) => {
          return this.sort_flag ? d3.descending(a, b) : d3.ascending(a, b);
        })
          .transition()
          .duration(500)
          .attr('x', (d, i) => {
            return this.x_scale(i);
          })


        this.svg.selectAll('text').sort((a, b) => {
          return this.sort_flag ? d3.descending(a, b) : d3.ascending(a, b);
        })
          .transition()
          .duration(500)
          .attr('x', (d, i) => {
            return this.x_scale(i) + this.x_scale.bandwidth() / 2;
          })

        this.sort_flag = !this.sort_flag;

      }
      ,
      initScale() {
        this.x_scale = d3.scaleBand().domain(d3.range(this.datalist.length))
          .range([this.padding, this.optinos.width - this.padding * 2]).paddingInner(0.1);

        this.y_scale = d3.scaleLinear()
          .domain([0, d3.max(this.datalist)])
          .range([this.padding, this.optinos.height - this.padding * 2])

      }

    }




    function mockData() {
      let datalist = [];
      for (let i = 0; i < 5; i++) {
        let num = Math.ceil(Math.random() * 450);
        datalist.push(num);
      }
      return datalist;
    }


    new Bar({
      el: '#app',
      width: 500,
      height: 400,
      data: mockData()
    })



  </script>


</body>

</html>